#lang scribble/doc
@(require scribble/manual scribble/eval "guide-utils.rkt")

@;{@title[#:tag "stx-certs" #:style 'quiet]{Syntax Taints}}
@title[#:tag "stx-certs" #:style 'quiet]{语法污染}

@;{A use of a macro can expand into a use of an identifier that is not
exported from the module that binds the macro. In general, such an
identifier must not be extracted from the expanded expression and used
in a different context, because using the identifier in a different
context may break invariants of the macro's module.}
宏的使用可以展开为未从绑定宏的模块导出的标识的使用。一般来说，这样的标识不能从展开表达式中提取出来并在不同的上下文中使用，因为在不同上下文中使用标识可能会破坏宏模块的不变量。

@;{For example, the following module exports a macro @racket[go] that
expands to a use of @racket[unchecked-go]:}
例如，下面的模块导出一个宏@racket[go]，它展开为使用@racket[unchecked-go]：

@racketmod[
#:file "m.rkt"
racket
(provide go)

(define (unchecked-go n x) 
  (code:comment @#,t{@;{to avoid disaster, @racket[n] must be a number}为了避免灾难，@racket[n]必须是数字})
  (+ n 17))

(define-syntax (go stx)
  (syntax-case stx ()
   [(_ x)
    #'(unchecked-go 8 x)]))
]

@;{If the reference to @racket[unchecked-go] is extracted from the
expansion of @racket[(go 'a)], then it might be inserted into a new
expression, @racket[(unchecked-go #f 'a)], leading to disaster. The
@racket[datum->syntax] procedure can be used similarly to construct
references to an unexported identifier, even when no macro expansion
includes a reference to the identifier.}
如果从@racket[(go 'a)]展开中解析了对@racket[unchecked-go]的引用，那么它可能会被插入到一个新的表达式@racket[(unchecked-go #f 'a)]中速，从而导致灾难。@racket[datum->syntax]过程同样可以类似地用于构建对未报告标识的引用，即使没有宏展开包括对标识的引用。

@;{To prevent such abuses of unexported identifiers, the @racket[go]
macro must explicitly protect its expansion by using
@racket[syntax-protect]:}
为了防止滥用未报告的标识，@racket[go]宏必须使用@racket[syntax-protect]明确保护其展开：

@racketblock[
(define-syntax (go stx)
  (syntax-case stx ()
   [(_ x)
    (syntax-protect #'(unchecked-go 8 x))]))
]

@;{The @racket[syntax-protect] function causes any syntax object that is
extracted from the result of @racket[go] to be @deftech{tainted}.  The
macro expander rejects tainted identifiers, so attempting to extract
@racket[unchecked-go] from the expansion of @racket[(go 'a)] produces
an identifier that cannot be used to construct a new expression (or, at
least, not one that the macro expander will accept). The
@racket[syntax-rules], @racket[syntax-id-rule], and
@racket[define-syntax-rule] forms automatically protect their
expansion results.}
@racket[syntax-protect]函数会使从@racket[go]结果中提取的任何语法对象成为@deftech{污染}。宏展开器拒绝受污染的标识，因此试图从@racket[(go 'a)]的展开中提取@racket[unchecked-go]会产生一个标识，该标识不能用于构造新的表达式（或者至少宏展开器不会将接受）。@racket[syntax-rules]、@racket[syntax-id-rule]和@racket[define-syntax-rule]表自动保护它们的展开结果。

@;{More precisely, @racket[syntax-protect] @deftech{arms} a syntax object
with a @deftech{dye pack}. When a syntax object is armed, then
@racket[syntax-e] taints any syntax object in its result. Similarly,
@racket[datum->syntax] taints its result when its first argument is
armed. Finally, if any part of a quoted syntax object is armed, then
the corresponding part is tainted in the resulting syntax constant.}
更准确地说，@racket[syntax-protect] @deftech{装备}了一个带@deftech{染料包（dye pack）}的语法对象。当一个语法对象被装备起来时，@racket[syntax-e]会在其结果中污染任何语法对象。同样，@racket[datum->syntax]在其第一个参数被装备时会污染其结果。最后，如果引用的语法对象的任何部分被装备，那么相应的部分就会在结果语法常量中受到污染。

@;{Of course, the macro expander itself must be able to @deftech{disarm}
a taint on a syntax object, so that it can further expand an
expression or its sub-expressions. When a syntax object is armed with
a dye pack, the dye pack has an associated inspector that can be used
to disarm the dye pack. A @racket[(syntax-protect _stx)] function call
is actually a shorthand for @racket[(syntax-arm _stx #f #t)], which
arms @racket[_stx] using a suitable inspector. The expander uses
@racket[syntax-disarm] and with its inspector on every expression
before trying to expand or compile it.}
当然，宏展开器本身必须能够@deftech{解除}语法对象上的污染，以便进一步展开表达式或其子表达式。当语法对象装备有一个染料包时，染料包具有可用于解除染料包的关联检查器。@racket[(syntax-protect _stx)]函数调用实际上是@racket[(syntax-arm _stx #f #t)]的简写，它使用合适的检查器对@racket[_stx]进行检查。在试图展开或编译每个表达式之前，使用@racket[syntax-disarm]和它的检查器。

@;{In much the same way that the macro expander copies properties from a
syntax transformer's input to its output (see @refsecref["stxprops"]),
the expander copies dye packs from a transformer's input to its
output. Building on the previous example,}
与宏展开器将属性从语法转换器的输入复制到其输出（请参见《@refsecref["stxprops"]》（语法对象属性））的方式大致相同，展开器会将染料包从转换器的输入拷贝到其输出。基于前面的示例，

@racketmod[
#:file "n.rkt"
racket
(require "m.rkt")

(provide go-more)

(define y 'hello)

(define-syntax (go-more stx)
  (syntax-protect #'(go y)))
]

@;{the expansion of @racket[(go-more)] introduces a reference to the
unexported @racket[y] in @racket[(go y)], and the expansion result is
armed so that @racket[y] cannot be extracted from the expansion.  Even
if @racket[go] did not use @racket[syntax-protect] for its result
(perhaps because it does not need to protect @racket[unchecked-go]
after all), the dye pack on @racket[(go y)] is propagated to the final
expansion @racket[(unchecked-go 8 y)]. The macro expander uses
@racket[syntax-rearm] to propagate dye packs from a transformer's
input to its output.}
@racket[(go-more)]的展开引入了对@racket[(go y)]中未报告的@racket[y]的引用，并且展开结果是装备起来的，因此无法从展开中提取@racket[y]。即使@racket[go]没有使用@racket[syntax-protect]作为其结果（也许是因为它根本不需要保护@racket[unchecked-go]），@racket[(go y)]上的染色包也会传播到最终展开@racket[(unchecked-go 8 y)]。宏展开器使用@racket[syntax-rearm]将染料包从转换器的输入传播到其输出。

@;------------------------------------------------------------------------
@;{@section{Tainting Modes}}
@section[#:tag "Tainting-Modes"]{污染模式}

@;{In some cases, a macro implementor intends to allow limited
destructuring of a macro result without tainting the result.
For example, given the following @racket[define-like-y]
macro,}
在某些情况下，宏实现者打算允许对宏结果进行有限的破坏，而不会影响结果。例如，给定以下@racket[define-like-y]宏，

@racketmod[
#:file "q.rkt"
racket

(provide define-like-y)

(define y 'hello)

(define-syntax (define-like-y stx)
  (syntax-case stx ()
    [(_ id) (syntax-protect #'(define-values (id) y))]))
]

@;{someone may use the macro in an internal definition:}
有人可能在内部定义中使用宏：

@racketblock[
(let ()
  (define-like-y x)
  x)
]

@;{The implementor of the @filepath{q.rkt} module most likely intended to allow
such uses of @racket[define-like-y]. To convert an internal definition
into a @racket[letrec] binding, however, the @racket[define] form
produced by @racket[define-like-y] must be deconstructed, which would
normally taint both the binding @racket[x] and the reference to
@racket[y].}
@filepath{q.rkt}模块的实现者很可能打算允许使用@racket[define-like-y]。然而，要将内部定义转换为@racket[letrec]绑定，必须解构@racket[define-like-y]生成的的@racket[define]表，这通常会污染绑定的@racket[x]和对@racket[y]的引用。

@;{Instead, the internal use of @racket[define-like-y] is allowed,
because @racket[syntax-protect] treats specially a syntax list that
begins with @racket[define-values]. In that case, instead of arming
the overall expression, each individual element of the syntax list is
armed, pushing dye packs further into the second element of the list so
that they are attached to the defined identifiers. Thus,
@racket[define-values], @racket[x], and @racket[y] in the expansion
result @racket[(define-values (x) y)] are individually armed, and the
definition can be deconstructed for conversion to @racket[letrec].}
相反，允许在内部使用@racket[define-like-y]，因为@racket[syntax-protect]专门处理以@racket[define-values]开头的语法列表。在这种情况下，不是装备整个表达式，而是装备语法列表的每个单独元素，将染料包进一步推入列表的第二个元素，以便将它们附加到定义的标识。因此，展开结果@racket[(define-values (x) y)]中的@racket[define-values]、@racket[x]和@racket[y]是单独被装备的，并且可以对定义进行解构以转换为@racket[letrec]。

@;{Just like @racket[syntax-protect], the expander rearms a transformer
result that starts with @racket[define-values], by pushing dye packs
into the list elements. As a result, @racket[define-like-y] could have
been implemented to produce @racket[(define id y)], which uses
@racket[define] instead of @racket[define-values]. In that case, the
entire @racket[define] form is at first armed with a dye pack, but as the
@racket[define] form is expanded to @racket[define-values], the dye
pack is moved to the parts.}
就像@racket[syntax-protect]一样，展开器通过将染料包推送到列表元素中，重新排列以@racket[define-values]开头的转换结果。因此，@racket[define-like-y]可以实现为生成@racket[(define id y)]，它使用@racket[define]而不是@racket[define-values]。在这种情况下，整个@racket[define]表首先装备一个染料包，但是当@racket[define]表展开为@racket[define-values]时，染料包会移动到各个部分。

@;{The macro expander treats syntax-list results starting with
@racket[define-syntaxes] in the same way that it treats results
starting with @racket[define-values]. Syntax-list results starting
with @racket[begin] are treated similarly, except that the second
element of the syntax list is treated like all the other elements
(i.e., the immediate element is armed, instead of its
content). Furthermore, the macro expander applies this special
handling recursively, in case a macro produces a @racket[begin] form
that contains nested @racket[define-values] forms.}
宏展开器处理以@racket[define-values]开头的语法列表结果的方式与处理以@racket[define-syntaxes]开头结果的方式相同。以@racket[begin]开头的语法列表结果被类似地处理，只是语法列表的第二个元素被视为所有其它元素（即，立即元素被装备，而不是其内容）。此外，宏展开器递归地应用这种特殊处理，以防宏生成包含嵌套@racket[define-values]表的@racket[begin]表。

@;{The default application of dye packs can be overridden by attaching
a @racket['taint-mode] property (see @refsecref["stxprops"]) to the
resulting syntax object of a macro transformer. If the property value is
@racket['opaque], then the syntax object is armed and not its
parts. If the property value is @racket['transparent], then the
syntax object's parts are armed. If the property value is
@racket['transparent-binding], then the syntax object's parts and
the sub-parts of the second part (as for @racket[define-values] and
@racket[define-syntaxes]) are armed. The @racket['transparent] and
@racket['transparent-binding] modes trigger recursive property
checking at the parts, so that armings can be pushed arbitrarily deeply
into a transformer's result.}
染料包的默认应用程序可以通过将@racket['taint-mode]属性（见《@refsecref["stxprops"]》（语法对象属性））附加到宏转换器的结果语法对象来覆盖。如果属性值是@racket['opaque]，那么语法对象被装备而且不是它的部件。如果属性值为@racket['transparent]，则语法对象的各个部分都是装备的。如果属性值是@racket['transparent-binding]，那么语法对象的部件和第二个部件的子部件（如@racket[define-values]和@racket[define-syntaxes]）被装备。@racket['transparent]和@racket['transparent-binding]模式会在零件上触发递归属性检查，因此可以任意深入地将防护推送到变换器的结果中。

@;------------------------------------------------------------------------
@;{@section[#:tag "taints+code-inspectors"]{Taints and Code Inspectors}}
@section[#:tag "taints+code-inspectors"]{污染和代码检查}

@;{Tools that are intended to be privileged (such as a debugging
transformer) must disarm dye packs in expanded programs.  Privilege is
granted through @deftech{code inspectors}. Each dye pack records an
inspector, and a syntax object can be disarmed using a sufficiently
powerful inspector.}
用于特权的工具（如调试变换器）必须解除展开器中的染料包。权限通过 @deftech{代码检查器}授予。每个染料包记录一个检查器，并且可以使用足够强大的检查器解除语法对象。

@;{When a module is declared, the declaration captures the current value
of the @racket[current-code-inspector] parameter.  The captured
inspector is used when @racket[syntax-protect] is applied by a macro
transformer that is defined within the module. A tool can disarm the
resulting syntax object by supplying @racket[syntax-disarm] with
an inspector that is the same or a super-inspector of the module's
inspector. Untrusted code is ultimately run after setting
@racket[current-code-inspector] to a less powerful inspector (after
trusted code, such as debugging tools, have been loaded).}
当声明一个模块时，声明会捕获@racket[current-code-inspector]参数的当前值。当模块中定义的宏转换器应用@racket[syntax-protect]时，将使用捕获的检查器。一个工具可以通过向@racket[syntax-disarm]提供与模块检查器相同的检查器或超级检查器。不受信任的代码最终会在将@racket[current-code-inspector]设置为功能较弱的检查器后运行（在加载了可信代码（如调试工具）之后）。

@;{With this arrangement, macro-generating macros require some care,
since the generating macro may embed syntax objects in the generated
macro that need to have the generating module's protection level,
rather than the protection level of the module that contains the
generated macro. To avoid this problem, use the module's
declaration-time inspector, which is accessible as
@racket[(variable-reference->module-declaration-inspector
(#%variable-reference))], and use it to define a variant of
@racket[syntax-protect].}
通过这种安排，宏生成宏需要小心一些，因为生成宏可能会在生成的宏中嵌入语法对象，这些对象需要具有生成模块的保护级别，而不是包含生成宏的模块的保护级别。为了避免这个问题，请使用模块的声明时间检查器，该检查器可以作为@racket[(variable-reference->module-declaration-inspector
(#%variable-reference))]访问，并使用它来定义@racket[syntax-protect]的变体。

@;{For example, suppose that the @racket[go] macro is implemented through
a macro:}
例如，假设@racket[go]宏是通过一个宏实现的：

@racketmod[
racket
(provide def-go)

(define (unchecked-go n x) 
  (+ n 17))

(define-syntax (def-go stx)
  (syntax-case stx ()
    [(_ go)
     (protect-syntax
      #'(define-syntax (go stx)
          (syntax-case stx ()
            [(_ x)
             (protect-syntax #'(unchecked-go 8 x))])))]))
]

@;{When @racket[def-go] is used inside another module to define
@racket[go], and when the @racket[go]-defining module is at a
different protection level than the @racket[def-go]-defining module, the
generated macro's use of @racket[protect-syntax] is not right.  The
use of @racket[unchecked-go] should be protected at the level of the
@racket[def-go]-defining module, not the @racket[go]-defining module.}
当@racket[def-go]在另一个模块中用于定义@racket[go]时，以及当@racket[go]定义模块与@racket[def-go]定义模块处于不同的保护级别时，生成的宏使用@racket[protect-syntax]是不正确的。使用@racket[unchecked-go]应该在@racket[def-go]定义模块的级别上进行保护，而不是在@racket[go]定义模型的级别上。

@;{The solution is to define and use @racket[go-syntax-protect], instead:}
解决方案是定义并使用@racket[go-syntax-protect]，而不是：

@racketmod[
racket
(provide def-go)

(define (unchecked-go n x) 
  (+ n 17))

(define-for-syntax go-syntax-protect
  (let ([insp (variable-reference->module-declaration-inspector
               (#%variable-reference))])
    (lambda (stx) (syntax-arm stx insp))))

(define-syntax (def-go stx)
  (syntax-case stx ()
    [(_ go)
     (protect-syntax
      #'(define-syntax (go stx)
          (syntax-case stx ()
           [(_ x)
            (go-syntax-protect #'(unchecked-go 8 x))])))]))
]

@;------------------------------------------------------------------------
@;{@section[#:tag "code-inspectors+protect"]{Protected Exports}}
@section[#:tag "code-inspectors+protect"]{受保护的导出}

@;{Sometimes, a module needs to export bindings to some modules---other
modules that are at the same trust level as the exporting module---but
prevent access from untrusted modules. Such exports should use the
@racket[protect-out] form in @racket[provide]. For example,
@racket[ffi/unsafe] exports all of its unsafe bindings as
@deftech{protected} in this sense.}
有时，一个模块需要将绑定导出到一些模块——与导出模块处于相同信任级别的其他模块——但阻止来自不受信任模块的访问。此类导出应使用@racket[provide]中的@racket[protect-out]表。例如，在这个意义上，@racket[ffi/unsafe]将其所有不安全绑定导出为@deftech{受保护的（protected）}。

@;{Code inspectors, again, provide the mechanism for determining which
modules are trusted and which are untrusted. When a module is
declared, the value of @racket[current-code-inspector] is associated
to the module declaration. When a module is instantiated (i.e., when the
body of the declaration is actually executed), a sub-inspector is
created to guard the module's exports. Access to the module's
@tech{protected} exports requires a code inspector higher in the
inspector hierarchy than the module's instantiation inspector; note
that a module's declaration inspector is always higher than its
instantiation inspector, so modules are declared with the same code
inspector can access each other's exports.}
代码检查器同样提供了确定哪些模块是可信的，哪些模块是不可信的机制。当一个模块被声明时，@racket[current-code-inspector]的值与模块声明相关联。当一个模块被实例化时（即，当声明的主体被实际执行时），会创建一个子检查器来保护模块的导出。访问模块的@tech{受保护}导出需要在检查器层次结构中比模块的实例化检查器更高的代码检查器；请注意，模块的声明检查器始终高于其实例化检查器，因此使用相同的代码声明模块，检查器可以访问彼此的导出。

@;{Syntax-object constants within a module, such as literal identifiers
in a template, retain the inspector of their source module. In this
way, a macro from a trusted module can be used within an untrusted
module, and @tech{protected} identifiers in the macro expansion still
work, even through they ultimately appear in an untrusted
module. Naturally, such identifiers should be @tech{arm}ed, so that
they cannot be extracted from the macro expansion and abused by
untrusted code.}
模块中的语法对象常量（如模板中的文字标识）保留其源模块的检查器。通过这种方式，来自受信任模块的宏可以在不受信任的模块中使用，并且宏展开中的@tech{受保护}标识仍然有效，即使它们最终出现在不受信赖的模块中。当然，这些标识符应该是@tech{装备的}，以便它们不能从宏展开中提取出来并被不可信代码滥用。

@;{Compiled code from a @filepath{.zo} file is inherently untrustworthy,
unfortunately, since it can be synthesized by means other than
@racket[compile]. When compiled code is written to a @filepath{.zo}
file, syntax-object constants within the compiled code lose their
inspectors. All syntax-object constants within compiled code acquire
the enclosing module's declaration-time inspector when the code is
loaded.}
不幸的是，从@filepath{.zo}文件编译的代码本质上是不可信的，因为它可以通过@racket[compile]以外的方法进行合成。当编译代码被写入@filepath{.zo}文件的被编译代码本质上是不可信的。当编译后的代码写入到一个@filepath{.zo}文件时，编译代码中的语法对象常量会丢失其检查器。编译代码中的所有语法对象常量都会获取加载代码时封装模块的声明时间检查器。